Conversation
…imeout - PDF: replace ?raw blob URL with globalThis.pdfjsWorker bypass (avoids nginx MIME + blob import issues) - Sub-session: merge metadata updates in subsession.created handler instead of skipping existing IDs - P2P: always save hopTimeoutMinutes explicitly, fix null guards in send paths - Cleanup: remove duplicate P2P config block in SessionControls Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ndow - Add runtimeType to sessionInfo so SessionControls renders Stop for transport sessions - Expand UsageFooter render condition to include plan/quota badge metadata Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove subSessionsFull stripping memo, pass full SubSession[] to PanelRenderContext - Import SubSession type directly in PanelRenderContext (prevents future type drift) - Add compact UsageFooter to pinned SubSessionContent with model/plan/quota display - Document compact panel contract (intentional inclusions/exclusions) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…le store data - buildSubSessionSync: compute planLabel/quotaLabel/quotaUsageLabel fresh via getQwenDisplayMetadata (same as buildSessionList for main sessions) - refreshQwenQuotaUsageLabels: re-sync sub-sessions to browser on quota update - Revert parent-session inheritance hack — daemon provides fresh data directly Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Combine quotaLabel + quotaUsageLabel into single inline text (9px, gray) - Plan badge (Free/Paid/BYO) stays as pill badge on the right - Layout: "1,000/day · today 12/1000" [Free] Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Plan/quota (e.g. "1,000/day · today 12/1000 Free") now inline small text in shortcuts row, left of model switcher and Solo button - Removed from UsageFooter to avoid duplication - Pinned panel: compact inline badge row (no SessionControls there) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…refix On Windows with ConPTY, respawnSession was prepending `export IMCODES_SESSION='...'; ` to the command, then passing it through cmd.exe /c — which doesn't understand POSIX `export` syntax, causing the respawn to fail. Fix: extend respawnPane / conptyRespawnPane / conptyNewSession to accept an env map. When BACKEND === 'conpty', pass mergedEnv directly to conptyNewSession so node-pty injects it as proper process env vars. Keep the existing envPrefix bash-string path for tmux/wezterm. Tests: 4 new assertions lock the conptyRespawnPane env injection contract. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e flake on macOS codex-watcher-refresh: after writing newerSameUuid, explicitly advance its mtime +2s via utimes() so checkNewer() works correctly on HFS+ (1-second mtime granularity). Without this the test is flaky on macOS CI when both writes land within the same second. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
emitRecentHistory was added unconditionally to startWatchingSpecificFile (codex-watcher) and activateFile (jsonl-watcher) in the no-text refresh commit, causing a full replay of previous session messages whenever codex or claude-code sessions are respawned by the health poller. Fix: add replayHistory option (default false) to startWatchingSpecificFile, startWatchingFile, startWatching, and activateFile. Only the daemon-restart restore paths in restoreFromStore() pass replayHistory: true — these are the cases where the browser genuinely needs to see recent history after reconnecting. Session respawn/restart paths do not replay. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add regression tests to both codex-watcher and jsonl-watcher that assert: - startWatchingSpecificFile / startWatchingFile do NOT emit pre-existing content by default (replayHistory defaults to false) - Only when replayHistory: true is explicitly passed (daemon restore path) is historical content replayed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
History is persisted in the timeline store and served to browsers via the timeline replay endpoint. Replaying JSONL/rollout files at watcher startup was always wrong — it duplicates events the browser already received, regardless of whether the daemon or session restarted. Remove replayHistory option and all emitRecentHistory calls from startWatchingSpecificFile, startWatchingFile, startWatching, and activateFile. Watchers now exclusively track new content from fileOffset. Tests updated to assert: watcher startup never emits pre-existing content. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…m startup Export emitRecentHistory so tests can call it directly. Update emitRecentHistory and stable-eventId tests to not rely on watcher startup triggering history replay (which was removed per no-replay rule). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On macOS APFS (nanosecond mtime) fileB was newer than fileA because it was written a few ns later in beforeEach. watchFile saw fileB as newer and switched the 'jsonl-a' watcher away from fileA, so refresh() drained fileB (empty) and never emitted the expected event. Fix: create fileB first, then fileA, and use utimes to advance fileA's mtime +2s past fileB — same technique used for the codex-watcher HFS+ fix. Guards both nanosecond-precision APFS and 1-second HFS+ resolvers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…indows When the daemon launches via a Windows Scheduled Task or Startup shortcut, the restricted environment cannot resolve 'cmd.exe' through PATH, causing CreateProcess to fail with 'File not found' and all sessions to enter the error state with a runaway restart loop. Fix: resolve cmd.exe via COMSPEC env var (always set by Windows), falling back to %SystemRoot%\system32\cmd.exe. Also convert CWD backslashes to forward slashes to avoid node-pty's internal path.resolve error 267 on some versions. Tests: 3 new cases for COMSPEC resolution, fallback path, and CWD slash normalization. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
conpty: update 7 spawn assertions from bare 'cmd.exe' to stringMatching(/cmd\.exe$/i) so they pass on actual Windows where the absolute COMSPEC path is used (e.g. C:\Windows\system32\cmd.exe). p2p-orchestrator: afterEach now cancels all active runs before deleting the temp dir. Background async ops (idle polls, file reads) from parallel hops were still in flight when the dir was deleted, producing unhandled ENOENT promise rejections that Vitest classified as test failures. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…codex-watcher-retrack checkNewer() returns false when both files are created within the same second on HFS+ (1s resolution). Use utimes() to set runningFile mtime +2s past oldFile so maybeSwitchActiveFile() reliably detects it as newer on all platforms. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three root causes fixed: 1. **Duplicate watchdog loops**: restartWindowsDaemon only killed the daemon node process, leaving the old watchdog cmd.exe alive. The old watchdog respawned the daemon (with stale code) while a new watchdog was also spawned — causing duplicate loops and version-mismatch restarts. Fix: kill the entire watchdog process tree (taskkill /f /t) before spawning a fresh hidden watchdog. 2. **schtasks /Create with visible window**: bind created the scheduled task with a bare node command (/TR "node.exe ... start --foreground"), then tried /Change to VBS. If /Change failed silently, every login launched a visible cmd.exe window. Fix: create the task directly with /TR "wscript launcher.vbs". 3. **Upgrade cleanup popups**: upgrade batch used `start "" cmd /c cleanup` which flashes a visible window. Fix: use `start "" /min cmd /c cleanup` to minimize. Also: VBS launcher now prioritized over scheduled task in restart flow, and all spawn() calls use windowsHide: true. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…unused vars - Rewrite windows-daemon.test.ts to match new killWatchdogTree behavior (wmic parent lookup + taskkill /t tree-kill instead of bare taskkill) - Update test priority: VBS first, then schtask, then startup shortcut - Add test for force-kill fallback when tree-kill misses daemon - Remove unused nodeExe/imcodesScript in bind-flow (schtasks now uses VBS directly) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
im4codes
pushed a commit
that referenced
this pull request
Apr 21, 2026
…of being overridden by them
Observed failure: user set global rule "Always commit and push if asked!"
in the supervision defaults. A session hit idle with uncommitted work;
user asked "提交了么?" (did you commit?), agent answered "还没提交" (no).
Supervisor returned `complete` — rule was never enforced.
Root cause — two heuristics in the decision-prompt rule list were
structurally able to defeat any user rule:
1. "A factual answer to a user question ... is typically complete for
that turn; the user asked a question, the agent answered it. Do not
treat state reports as proposed work."
2. "A user-set supervision rule phrased conditionally ('if asked',
'when X') is conditional. Check whether the condition actually
fires in the current turn before using it to justify continue."
The arbiter LLM took "Always commit and push if asked!" at heuristic #2's
narrowest reading ("the user didn't literally command 'commit it' this
turn → condition didn't fire") and combined it with heuristic #1 to
justify `complete` on the Q-and-A turn. Result: the user's enforce-this
rule was silently downgraded to "advice the arbiter may ignore".
Fix — reorder and rewrite:
- New top-of-list clause: "USER-SET SUPERVISION RULES ARE AUTHORITATIVE."
This is the first decision rule the arbiter reads. It says the user-
rules block overrides the generic heuristics below it, gives concrete
worked examples for:
* commit/push rules (matches the current failure mode verbatim)
* blanket wording ("always", "每次", "必须", "绝不") → unconditional
* conditional wording ("if asked", "when X", "如果", "当") →
interpret GENEROUSLY in the user's favor: the topic appearing in
the conversation IS the condition firing.
- Heuristic #1 ("factual Q&A → complete") now explicitly reads
"typically complete for that turn IF no user-set rule applies" — so
it still covers ordinary questions but stops poaching turns that a
user rule governs.
- Heuristic #2 (the conditional-rule escape hatch) is removed; its
responsibility is folded into the authoritative clause, which now
owns all conditional-rule handling from the user-rules-always-win
side.
- Repair prompt mirrors the same clause so JSON-invalid fallbacks
can't drop back into the old behavior.
All 71 existing supervision prompt / config / broker tests stay green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
im4codes
pushed a commit
that referenced
this pull request
Apr 25, 2026
…"Agent working" for shell
Two related root causes for "shell terminal 断流, 必须刷新页面才能恢复":
1) Server's `handleQueueOverflow` called `removeBrowserSessionSubscription`
on every overflow. Heavy shell stdout (or a slow browser socket) created
a churn cycle:
stdout flood → 1MB queue full → overflow → server unsubscribes →
daemon stops pipe-pane → client receives stream_reset → resubscribes →
daemon restarts pipe → flood again → overflow again …
Each cycle bumped the client's resetState count; once cooldown engaged
(8 resets in 60s) the terminal sat frozen for 5s, and if output kept
flowing the cooldown re-engaged indefinitely. Refresh recovered because
it bypassed the accumulated cooldown state.
Fix: on overflow, send terminal.stream_reset and replace the per-(session,
ws) queue with a fresh accounting state, but DO NOT unsubscribe.
Subscription stays alive so the daemon never tears down its pipe and the
client receives subsequent frames immediately. Orphaned in-flight
callbacks from the old queue still decrement only their own counter
(harmless, GC'd later).
2) Shell + script sessions are not "agents" — they're plain terminals. The
daemon's terminal-streamer fires `session.state(running)` on any pipe
output (idle detection runs without a structured watcher), so running
`top`, `tail -f`, anything chatty, surfaced as "🤖 Agent working..." in
the footer. Confusing wording, and combined with #1's broken stream the
user saw a frozen terminal labeled "Agent working" — a worst-of-both-
worlds experience.
Fix: gate the entire UsageFooter live-status block on `agentType !==
'shell' && !== 'script'`. Also early-out shouldShowFooter for these
types so the footer doesn't render at all (shell has no token usage,
no quota, no cost — the footer was always content-empty for shell).
Client-side defensive change: on `terminal.stream_reset`, request a fresh
`terminal.snapshot_request` synchronously before the existing resubscribe
backoff fires. With #1 fixed the subscription is healthy across overflow,
so a snapshot is the cheapest way to redraw the dropped frames within one
round-trip. During cooldown we skip the request to avoid hammering the
server when it's already overwhelmed.
Regression tests added (7):
- server: overflow does NOT send terminal.unsubscribe to daemon, and
a fresh queue continues to forward subsequent frames
- server: overflow → fresh-queue path keeps the second forward alive
- ws-client: stream_reset fires terminal.snapshot_request synchronously
- ws-client: cooldown suppresses snapshot_request to avoid hammering
- usage-footer: shell session with sessionState=running shows no
"Agent working..." text and no live-status inline span
- usage-footer: same for script
- usage-footer: shell idle does not show "Agent idle..." either
Full suite: 3816 pass (was 3809), daemon test:unit clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
merge